package com.brayan.webapp.service.realestate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.Period;

import com.brayan.webapp.model.realestate.Reservation;
import com.brayan.webapp.model.realestate.Reservation_;

@Stateless
public class CalendarBean {

	@PersistenceContext
	EntityManager em;

	@Inject
	ReservationBean reservationService;

	private static final int WEEK_DAYS = 7;
	/**
	 * 20 weeks times 7 days.
	 */
	private static final int INITIAL_DAYS = 20 * WEEK_DAYS;
	/**
	 * 4 weeks extra.
	 */
	private static final int BLOCK_SIZE = 4 * WEEK_DAYS;

	public List<CalendarDay> getCalendarDays(int loadingBlock) {

		List<CalendarDay> calDays = new ArrayList<CalendarDay>();

		DateTime initDate = getInitDate(loadingBlock);
		// Ensure that the startDate is a Monday
		DateTime startDate = initDate.minusDays(initDate.getDayOfWeek() - 1);
		// Ensure that the endDate is a Sunday
		DateTime endDate = null;
		if (loadingBlock == 1)
			endDate = startDate.plus(Period.days(INITIAL_DAYS));
		else
			endDate = startDate.plus(Period.days(BLOCK_SIZE));

		int addedDays = 0;
		while (startDate.plus(Period.days(addedDays)).compareTo(endDate) < 0) {
			CalendarDay newCalDay = new CalendarDay();
			newCalDay.setDate(new LocalDate(startDate.plus(Period.days(addedDays))));
			calDays.add(newCalDay);
			addedDays++;
		}
		// Add reservation Data to the calendar Days.
		return enrichWithReservationData(calDays);
	}

	/**
	 * Get the a block of data where the first day is the first Monday.
	 * 
	 * @param loadingBlock
	 * @return The DateTime of the first Monday of the block.
	 */
	private DateTime getInitDate(int loadingBlock) {
		DateTime currentDate = new DateTime();
		if (loadingBlock == 1)
			return currentDate;
		else {
			int addedDaysToTheEnd = INITIAL_DAYS + ((loadingBlock - 2) * BLOCK_SIZE);
			return currentDate.plus(Period.days(addedDaysToTheEnd));
		}
	}

	/**
	 * Query reservation days and add it to the calendar days of the passed
	 * list.
	 * 
	 * @return
	 */
	private List<CalendarDay> enrichWithReservationData(List<CalendarDay> calendarDays) {

		// Get for every calendar day a reservation - if it exists.
		// Execute a single query: from date x to date y. Pick the first and the
		// last date.
		for (int i = 0; i < calendarDays.size(); i++) {
			CalendarDay calendarDay = calendarDays.get(i);
			Date iCalDate = calendarDay.getDateObject();
			// TODO. Must set to GMT!!!!
			List<Reservation> reservationList = getReservationByDate(iCalDate);

			for (Reservation reservation : reservationList) {
				calendarDay.addToReservationList(reservation);
				calendarDay.setNameOfTenant(reservation.getTenant().getGivenname().substring(0, 2)
						+ reservation.getTenant().getSurname().substring(0, 2));
			}
		}
		return calendarDays;
	}

	private List<Reservation> getReservationByDate(Date calendarDate) {
		CriteriaBuilder critBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Reservation> query = critBuilder.createQuery(Reservation.class);
		Root<Reservation> rootClass = query.from(Reservation.class);
		query.select(rootClass);
		ParameterExpression<Date> dateExpression = critBuilder.parameter(Date.class);
		Predicate predicate = critBuilder.between(dateExpression, rootClass.get(Reservation_.startDate),
				rootClass.get(Reservation_.endDate));
		query.where(predicate);
		TypedQuery<Reservation> typedQuery = em.createQuery(query);
		typedQuery.setParameter(dateExpression, calendarDate, TemporalType.TIMESTAMP);
		List<Reservation> reservationList = typedQuery.getResultList();

		return reservationList;
	}
}
